require(plotly)
Loading required package: plotly
Loading required package: ggplot2
Keep up to date with changes at https://www.tidyverse.org/blog/
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
require(tidyverse)
Loading required package: tidyverse
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ tibble  3.1.6     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.1.1     ✓ forcats 0.5.1
✓ purrr   0.3.4     
── Conflicts ───────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks plotly::filter(), stats::filter()
x dplyr::lag()    masks stats::lag()
require(cowplot)
Loading required package: cowplot

Goal

  • Analyze the latest batches of flow cytometry data to determine the contribution of different (matching) parts of ScPho4 and CgPho4 to their difference in Pho2 dependence.
  • Develop an analysis pipeline to perform QC, correction (if needed) and plotting the results.

Data

Merge the three batches

raw <- list(
  "03/15/22" = read_tsv("../data/20220315-EO-chimera-batch-1/20220329-gated-median-out.txt", show_col_types = FALSE),
  "03/22/22" = read_tsv("../data/20220322-EO-chimera-batch-2/20220327-gated-median-out.txt", show_col_types = FALSE),
  "03/29/22" = read_tsv("../data/20220329-EO-chimera-batch-3/20220330-gated-median-out.txt", show_col_types = FALSE)
)
dat <- bind_rows(raw, .id = "date") %>% 
  #separate(col = well, sep = 1, into = c("row", "col")) %>% 
  separate(col = sample, sep = "-", into = c("plasmid", "host", "rep"))

QC

1. Number of events per sample

dat %>% ggplot(aes(x = n_induction)) + geom_histogram(bins = 30, fill = "forestgreen") + facet_grid(host~date) + theme_gray(base_size = 14) #+ panel_border()

Discussion

  1. Majority of the experimental wells (not blank) have > 10000 events, with the exception of a few samples
  2. Remove the blank wells and proceed with QC

2. Background subtraction

Check the background fluorescence levels across different days.

dat %>% filter(host == "156") %>% 
  pivot_longer(BL1.H:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = date, y = intensity)) + geom_point(aes(shape = well)) +
  stat_summary(geom = "pointrange", color = "red", position = position_nudge(x = 0.1)) +
  facet_wrap(~parameter, scale = "free_y") +
  theme_bw(base_size = 14)
No summary function supplied, defaulting to `mean_se()`
No summary function supplied, defaulting to `mean_se()`
No summary function supplied, defaulting to `mean_se()`
No summary function supplied, defaulting to `mean_se()`

03/29/22 D9 showed higher background fluorescence than the others. after checking the gating result, I found that NA-156-2 sample in D9 had a slightly wider distribution, and the 2d cluster gate is also wider than the other two samples. The cell size normalized intensity doesn’t show the same pattern, suggesting that at least some of the difference could be due to cell size variation. Try just averaging the three blank wells and perform the background removal.

Subtract the background

bg <- dat %>% 
  filter(host == "156") %>% 
  group_by(date) %>% 
  summarize(across(FSC.H:nRFP, ~ round(mean(.x),1))) %>% 
  column_to_rownames(var = "date")

dat1 <- dat %>% 
  filter(host != "blank") %>% 
  select(date:host, FSC.H:nRFP) %>%
  mutate(
    BL1.H = BL1.H - bg[date, "BL1.H"],
    YL2.H = YL2.H - bg[date, "YL2.H"],
    nGFP = nGFP - bg[date, "nGFP"],
    nRFP = nRFP - bg[date, "nRFP"],
  )

Double check that the subtraction worked correctly

dat1 %>% filter(host == "156") %>% 
  pivot_longer(BL1.H:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = date, y = intensity)) + geom_point(aes(shape = well)) +
  stat_summary(geom = "pointrange", color = "red", position = position_nudge(x = 0.1)) +
  facet_wrap(~parameter, scale = "free_y") +
  theme_bw(base_size = 14)
No summary function supplied, defaulting to `mean_se()`
No summary function supplied, defaulting to `mean_se()`
No summary function supplied, defaulting to `mean_se()`
No summary function supplied, defaulting to `mean_se()`

# remove the yH156 samples
dat1 <- dat1 %>% 
  filter(host != "156") %>% 
  mutate(host = ordered(host, levels = c("555", "373"), labels = c("PHO2", "pho2∆")))

Export the background-subtracted values for later use with the shinyapp

write_tsv(dat1, "../shinyapp/20220412-bg-sub-data.tsv")

3. Consistency across plates.

Check the background-subtracted fluorescence values for the host strains (yH373 and yH555) as well as the positive control strain (pH188 in yH373 or yH555 backgrounds), both of which are present on each plate.

dat1 %>% filter(plasmid %in% c("188", "NA")) %>% 
  pivot_longer(BL1.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  #mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = plasmid, y = intensity, color = date)) + 
  geom_point(position = position_dodge(0.5)) + 
  scale_color_brewer(type = "qual", palette = 2) +
  facet_grid(parameter~host, scale = "free_y") +
  theme_bw(base_size = 14) +
  theme(axis.text.x = element_text(face = 3))

  1. The host strain fluorescence levels appear to be consistent across the three days. Additionally, their GFP levels are zero, as expected.
  2. The positive control strain has strong BL1.H and correspondingly has strong YL2.H.

The size normalized values show slightly lower variance:

dat1 %>% filter(plasmid %in% c("188", "NA")) %>% 
  pivot_longer(nGFP:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  #mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = plasmid, y = intensity, color = date)) + 
  geom_point(position = position_dodge(0.5)) + 
  scale_color_brewer(type = "qual", palette = 2) +
  facet_grid(parameter~host, scale = "free_y") +
  theme_bw(base_size = 14) +
  theme(axis.text.x = element_text(face = 3))

4. Well position

Rationale

  • In my original plate design, I placed a positive control strain (envisioned CgPho4-mNeon in PHO2 background for example) in the control columns (1, 5, 9) on every other row (A, C, E, G). The reason for this is to use it to identify any well position effect on the fluorescence readings.
  • In Emily’s implementation, she instead put a pair of strains, namely pH188-yH323 and pH188-yH555 in these wells. This means I cannot use the positive control wells exactly the way as I designed them. But I can still use them to spot any trend.
dat1 %>% 
  filter(plasmid == "188") %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  pivot_longer(BL1.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  ggplot(aes(x = row, y = intensity, group = date)) + 
  geom_point(aes(color = date), size = 1) + 
  #geom_line(aes(color = date), size = 0.4) +
  stat_summary(aes(color = date), geom = "line", fun = mean) +
  scale_color_brewer(type = "qual", palette = 2) +
  facet_grid(parameter~host, scales = "free_y") +
  theme_bw(base_size = 14)

  • no obivous trend between the rows (or columns, not shown)
  • a clear correlation between YL2.H and BL1.H. at least part of this is due to the cell size differences – see size normalized data below:
dat1 %>% 
  filter(plasmid == "188") %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  pivot_longer(c(YL2.H, nRFP), names_to = "parameter", values_to = "intensity") %>% 
  ggplot(aes(x = row, y = intensity, group = date)) + 
  geom_point(aes(color = date), size = 1) + 
  #geom_line(aes(color = date), size = 0.4) +
  stat_summary(aes(color = date), geom = "line", fun = mean) +
  scale_color_brewer(type = "qual", palette = 2) +
  facet_grid(parameter~host, scales = "free_y") +
  theme_bw(base_size = 14)

  • What we care about is the ratio between Pho4-mNeon and PHO5pr-mCherry
dat1 %>% 
  filter(plasmid == "188") %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  ggplot(aes(x = BL1.H, y = YL2.H)) + 
  geom_point(aes(shape = date, color = row), size = 3) + 
  stat_smooth(method = "lm", se = FALSE, color = "gray50", size = 0.5) +
  scale_color_viridis_d() +
  scale_shape_manual(values = 15:17) +
  #scale_x_log10() + scale_y_log10() +
  #facet_grid(parameter~host, scales = "free_y") +
  theme_bw(base_size = 14)
`geom_smooth()` using formula 'y ~ x'

  • There is variation in BL1.H and correspondingly in YL2.H, but the ratio between the two are very consistent

Let’s check both ScPho4 (pH194) and CgPho4 (pH188) to see if their behaviors are consistent across the days.

dat1 %>% 
  filter(plasmid %in% c("188", "194")) %>% 
  mutate(`YL2.H/BL1.H` = YL2.H/BL1.H,
  #mutate(`YL2.H/BL1.H` = nRFP/nGFP,
         Pho4 = factor(plasmid, levels = c("194", "188"), 
                       labels = c("ScPho4", "CgPho4"))) %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  ggplot(aes(x = host, y = `YL2.H/BL1.H`)) + 
  geom_point(color = alpha("gray50", 0.9), position = position_jitter(0.2)) + 
  stat_summary(fun.data = "mean_se", geom = "pointrange", color = "red") +
  facet_grid(Pho4~date) +
  theme_bw(base_size = 14)

5. High variance samples

Summarize the background subtracted data by calculating the means and cv for each strain.

dat2 <- dat1 %>% 
  select(-nGFP, -nRFP) %>% 
  pivot_longer(FSC.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  group_by(date, plasmid, host, parameter) %>% 
  summarize(
    n = n(),
    mean = num(mean(intensity), digits = 0),
    cv = num(sd(intensity)/mean(intensity), digits = 2)
  ) %>% 
  arrange(desc(cv))
`summarise()` has grouped output by 'date', 'plasmid', 'host'. You can override using the `.groups` argument.
dat2 %>% 
  filter(plasmid != "NA", parameter != "FSC.H") %>% 
  ggplot(aes(x = cv)) + geom_histogram(aes(y = ..density../20)) + stat_ecdf() + 
  geom_hline(yintercept = 0.8, linetype = 2) +
  ylab("cumulative density") + 
  facet_wrap(~parameter) +
  theme_cowplot()
Don't know how to automatically pick scale for object of type pillar_num/pillar_vctr/vctrs_vctr/double. Defaulting to continuous.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

The histogram’s y-axis is not shown. The line graph represents the empirical CDF, and the dotted horizontal line is at 80%. GFP is more variable than RFP, likely because the absolute values of the former is lower. For both, ~80% of the samples have a CV < 20%.

Do the same for the cell size-normalized values

ndat2 <- dat1 %>% 
  select(-BL1.H, -YL2.H) %>% 
  pivot_longer(nGFP:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  group_by(date, plasmid, host, parameter) %>% 
  summarize(
    n = n(),
    mean = num(mean(intensity), digits = 0),
    cv = num(sd(intensity)/mean(intensity), digits = 2)
  ) %>% 
  arrange(desc(cv))
`summarise()` has grouped output by 'date', 'plasmid', 'host'. You can override using the `.groups` argument.
ndat2 %>% 
  filter(plasmid != "NA", parameter != "FSC.H") %>% 
  ggplot(aes(x = cv)) + geom_histogram(aes(y = ..density../20)) + stat_ecdf() + 
  geom_hline(yintercept = 0.8, linetype = 2) +
  ylab("cumulative density") + 
  facet_wrap(~parameter) +
  theme_cowplot()
Don't know how to automatically pick scale for object of type pillar_num/pillar_vctr/vctrs_vctr/double. Defaulting to continuous.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

CV is smaller on the cell size normalized fluorescence values.

Main

Plotting functions

Here I’d like to develop a series of plotting functions that take the names or any part of the Pho4 chimera annotation as input and plot their results in a variety of ways Load the Pho4 plasmid information and merge with the reshaped data

meta <- read_tsv("../data/20220330-chimera-Pho4-makeup.tsv", col_types = "cccc")

Plotting function

# subset data
tmp <- meta %>% 
  filter(symbol %in% c(refs,test)) %>% 
  inner_join(dat1, by = "plasmid") %>% 
  mutate(symbol = factor(symbol, levels = c(refs, test)))
Error: Problem with `mutate()` column `symbol`.
ℹ `symbol = factor(symbol, levels = c(refs, test))`.
x factor level [3] is duplicated
Run `rlang::last_error()` to see where the error occurred.

Plot individual components

tmp %>% 
  pivot_longer(nGFP:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  mutate(parameter = ordered(parameter, levels = c("nRFP", "nGFP"))) %>% 
  #pivot_longer(BL1.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  #mutate(parameter = ordered(parameter, levels = c("YL2.H", "BL1.H"))) %>% 
  ggplot(aes(x = symbol, y = intensity, group = host)) + 
  geom_bar(aes(fill = host), width = 0.5, alpha = 0.8,
           stat = "summary", fun = "mean", position = position_dodge(0.5)) +
  geom_point(aes(color = host), size = 1,
             position = position_jitterdodge(dodge.width = 0.5)) + 
  scale_color_manual(values = c("PHO2" = "gray20", "pho2∆" = "gray40")) +
  scale_fill_viridis_d(begin = 0.2, end = 0.6) +
  #stat_summary(fun = "mean", geom = "crossbar", color = "red", width = 0.25,
  #             position = position_dodge(0.75), ) +
  facet_wrap(~parameter, scale = "free_y", ncol = 1) +
  xlab("Pho4 chimera") + expand_limits(y = 0) +
  theme_gray(base_size = 14) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"))

tmp %>% 
  #mutate(`YL2.H/BL1.H` = YL2.H/BL1.H,
  mutate(`R/G` = nRFP/nGFP) %>% 
  ggplot(aes(x = symbol, y = `R/G`, group = host)) + 
  geom_bar(aes(fill = host), width = 0.5, alpha = 0.8,
           stat = "summary", fun = "mean", position = position_dodge(0.5)) +
  geom_point(aes(color = host), position = position_jitterdodge(dodge.width = 0.5)) + 
  scale_color_manual(values = c("PHO2" = "gray20", "pho2∆" = "gray40")) +
  scale_fill_viridis_d(begin = 0.2, end = 0.6) +
  #stat_summary(fun = "mean", color = "red", geom = "crossbar", width = 0.2,
  #             position = position_dodge(0.75), ) +
  theme_gray(base_size = 14) + xlab("Pho4 chimera") +
  theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"))

Next idea is to plot the ratio between the mean R/G for PHO2 and mean R/G for pho2∆, over the mean R/G for PHO2

First summarize the data.

datsum <- dat1 %>%
  filter(plasmid != "NA") %>% 
  mutate(`R/G` = YL2.H/BL1.H, `nR/G` = nRFP/nGFP) %>% 
  group_by(date, plasmid, host) %>% 
  summarize(across(c(BL1.H, nGFP, YL2.H, nRFP, `R/G`, `nR/G`), mean), .groups = "drop") %>% 
  pivot_wider(names_from = host, values_from = BL1.H:`nR/G`) %>% 
  mutate(`pho2∆/PHO2` = `R/G_pho2∆`/`R/G_PHO2`,
         `n.pho2∆/PHO2` = `nR/G_pho2∆`/`nR/G_PHO2`)

# useful to set a flag for low activity mutants
low.act.th <- datsum %>% filter(plasmid == "194") %>% summarize(across(.cols = where(is.numeric), .fns = mean)) %>% unlist()

Then extract the subset for plotting

tmpsum <- meta %>% 
  filter(symbol %in% c(refs,test)) %>% 
  inner_join(datsum, by = "plasmid") %>% 
  mutate(symbol = factor(symbol, levels = c(refs, test)))

Design the plot

tmpsum %>% 
  mutate(Activity = ifelse(`R/G_PHO2` < 2*low.act.th["R/G_pho2∆"], "low", "pass")) %>% 
  ggplot(aes(x = symbol, y = `pho2∆/PHO2`)) + 
  geom_col(aes(group = date, fill = `R/G_PHO2`), width = 0.75, color = "gray50",
           position = position_dodge(0.9)) +
  #geom_point(aes(color = host), position = position_jitterdodge(dodge.width = 0.5)) + 
  scale_fill_gradient2("Activity") +
  #scale_color_manual(values = c(alpha("black",0), "red3")) +
  #stat_summary(fun = "mean", color = "red", geom = "crossbar", width = 0.2,
  #             position = position_dodge(0.75), ) +
  facet_grid(.~Activity, scales = "free_x", space = "free_x", labeller = "label_both") +
  theme_bw(base_size = 14) + xlab("Pho4 chimera") +
  theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"))

X-Y plot

p3 <- tmpsum %>% 
  mutate(`nR/G_PHO2` = signif(`nR/G_PHO2`, digits = 2),
         `nR/G_pho2∆` = signif(`nR/G_pho2∆`, digits = 2)) %>% 
  ggplot(aes(x = `nR/G_PHO2`, y = `nR/G_pho2∆`, label = symbol)) + 
  geom_point(size = 2.5) + geom_abline(slope = 1) +
  theme_gray(base_size = 14)
ggplotly(p3, tooltip = c("label", "x", "y"))
LS0tCnRpdGxlOiAiRTAxMyBQaG80IGNoaW1lcmEgYWN0aXZpdHkgYW5hbHlzaXMsIG5ldyBob3N0IgphdXRob3I6IEJpbiBIZQpkYXRlOiAiMjAyMi0wMy0zMCB1cGRhdGVkIGByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKYGBge3J9CnJlcXVpcmUocGxvdGx5KQpyZXF1aXJlKHRpZHl2ZXJzZSkKcmVxdWlyZShjb3dwbG90KQpgYGAKCiMgR29hbAotIEFuYWx5emUgdGhlIGxhdGVzdCBiYXRjaGVzIG9mIGZsb3cgY3l0b21ldHJ5IGRhdGEgdG8gZGV0ZXJtaW5lIHRoZSBjb250cmlidXRpb24gb2YgZGlmZmVyZW50IChtYXRjaGluZykgcGFydHMgb2YgU2NQaG80IGFuZCBDZ1BobzQgdG8gdGhlaXIgZGlmZmVyZW5jZSBpbiBQaG8yIGRlcGVuZGVuY2UuIAotIERldmVsb3AgYW4gYW5hbHlzaXMgcGlwZWxpbmUgdG8gcGVyZm9ybSBRQywgY29ycmVjdGlvbiAoaWYgbmVlZGVkKSBhbmQgcGxvdHRpbmcgdGhlIHJlc3VsdHMuCgojIERhdGEKTWVyZ2UgdGhlIHRocmVlIGJhdGNoZXMKYGBge3J9CnJhdyA8LSBsaXN0KAogICIwMy8xNS8yMiIgPSByZWFkX3RzdigiLi4vZGF0YS8yMDIyMDMxNS1FTy1jaGltZXJhLWJhdGNoLTEvMjAyMjAzMjktZ2F0ZWQtbWVkaWFuLW91dC50eHQiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSwKICAiMDMvMjIvMjIiID0gcmVhZF90c3YoIi4uL2RhdGEvMjAyMjAzMjItRU8tY2hpbWVyYS1iYXRjaC0yLzIwMjIwMzI3LWdhdGVkLW1lZGlhbi1vdXQudHh0Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSksCiAgIjAzLzI5LzIyIiA9IHJlYWRfdHN2KCIuLi9kYXRhLzIwMjIwMzI5LUVPLWNoaW1lcmEtYmF0Y2gtMy8yMDIyMDMzMC1nYXRlZC1tZWRpYW4tb3V0LnR4dCIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCikKZGF0IDwtIGJpbmRfcm93cyhyYXcsIC5pZCA9ICJkYXRlIikgJT4lIAogICNzZXBhcmF0ZShjb2wgPSB3ZWxsLCBzZXAgPSAxLCBpbnRvID0gYygicm93IiwgImNvbCIpKSAlPiUgCiAgc2VwYXJhdGUoY29sID0gc2FtcGxlLCBzZXAgPSAiLSIsIGludG8gPSBjKCJwbGFzbWlkIiwgImhvc3QiLCAicmVwIikpCmBgYAoKIyBRQwojIyAxLiBOdW1iZXIgb2YgZXZlbnRzIHBlciBzYW1wbGUKYGBge3J9CmRhdCAlPiUgZ2dwbG90KGFlcyh4ID0gbl9pbmR1Y3Rpb24pKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCwgZmlsbCA9ICJmb3Jlc3RncmVlbiIpICsgZmFjZXRfZ3JpZChob3N0fmRhdGUpICsgdGhlbWVfZ3JheShiYXNlX3NpemUgPSAxNCkgIysgcGFuZWxfYm9yZGVyKCkKYGBgCgoqKl9EaXNjdXNzaW9uXyoqCgoxLiBNYWpvcml0eSBvZiB0aGUgZXhwZXJpbWVudGFsIHdlbGxzIChub3QgYmxhbmspIGhhdmUgPiAxMDAwMCBldmVudHMsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiBhIGZldyBzYW1wbGVzIGByIGRhdCAlPiUgZmlsdGVyKGhvc3QgIT0gImJsYW5rIiwgbl9pbmR1Y3Rpb24gPCAxMDAwMClgCjEuIFJlbW92ZSB0aGUgYmxhbmsgd2VsbHMgYW5kIHByb2NlZWQgd2l0aCBRQwoKIyMgMi4gQmFja2dyb3VuZCBzdWJ0cmFjdGlvbgoKQ2hlY2sgdGhlIGJhY2tncm91bmQgZmx1b3Jlc2NlbmNlIGxldmVscyBhY3Jvc3MgZGlmZmVyZW50IGRheXMuCmBgYHtyfQpkYXQgJT4lIGZpbHRlcihob3N0ID09ICIxNTYiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKEJMMS5IOm5SRlAsIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgbXV0YXRlKHBhcmFtZXRlciA9IG9yZGVyZWQocGFyYW1ldGVyLCBsZXZlbHMgPSBjKCJCTDEuSCIsICJZTDIuSCIsICJuR0ZQIiwgIm5SRlAiKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gaW50ZW5zaXR5KSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHdlbGwpKSArCiAgc3RhdF9zdW1tYXJ5KGdlb20gPSAicG9pbnRyYW5nZSIsIGNvbG9yID0gInJlZCIsIHBvc2l0aW9uID0gcG9zaXRpb25fbnVkZ2UoeCA9IDAuMSkpICsKICBmYWNldF93cmFwKH5wYXJhbWV0ZXIsIHNjYWxlID0gImZyZWVfeSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkKYGBgCgo+IDAzLzI5LzIyIEQ5IHNob3dlZCBoaWdoZXIgYmFja2dyb3VuZCBmbHVvcmVzY2VuY2UgdGhhbiB0aGUgb3RoZXJzLiBhZnRlciBjaGVja2luZyB0aGUgZ2F0aW5nIHJlc3VsdCwgSSBmb3VuZCB0aGF0IE5BLTE1Ni0yIHNhbXBsZSBpbiBEOSBoYWQgYSBzbGlnaHRseSB3aWRlciBkaXN0cmlidXRpb24sIGFuZCB0aGUgMmQgY2x1c3RlciBnYXRlIGlzIGFsc28gd2lkZXIgdGhhbiB0aGUgb3RoZXIgdHdvIHNhbXBsZXMuIFRoZSBjZWxsIHNpemUgbm9ybWFsaXplZCBpbnRlbnNpdHkgZG9lc24ndCBzaG93IHRoZSBzYW1lIHBhdHRlcm4sIHN1Z2dlc3RpbmcgdGhhdCBhdCBsZWFzdCBzb21lIG9mIHRoZSBkaWZmZXJlbmNlIGNvdWxkIGJlIGR1ZSB0byBjZWxsIHNpemUgdmFyaWF0aW9uLiBUcnkganVzdCBhdmVyYWdpbmcgdGhlIHRocmVlIGJsYW5rIHdlbGxzIGFuZCBwZXJmb3JtIHRoZSBiYWNrZ3JvdW5kIHJlbW92YWwuCgpTdWJ0cmFjdCB0aGUgYmFja2dyb3VuZApgYGB7cn0KYmcgPC0gZGF0ICU+JSAKICBmaWx0ZXIoaG9zdCA9PSAiMTU2IikgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JSAKICBzdW1tYXJpemUoYWNyb3NzKEZTQy5IOm5SRlAsIH4gcm91bmQobWVhbigueCksMSkpKSAlPiUgCiAgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJkYXRlIikKCmRhdDEgPC0gZGF0ICU+JSAKICBmaWx0ZXIoaG9zdCAhPSAiYmxhbmsiKSAlPiUgCiAgc2VsZWN0KGRhdGU6aG9zdCwgRlNDLkg6blJGUCkgJT4lCiAgbXV0YXRlKAogICAgQkwxLkggPSBCTDEuSCAtIGJnW2RhdGUsICJCTDEuSCJdLAogICAgWUwyLkggPSBZTDIuSCAtIGJnW2RhdGUsICJZTDIuSCJdLAogICAgbkdGUCA9IG5HRlAgLSBiZ1tkYXRlLCAibkdGUCJdLAogICAgblJGUCA9IG5SRlAgLSBiZ1tkYXRlLCAiblJGUCJdLAogICkKYGBgCgpEb3VibGUgY2hlY2sgdGhhdCB0aGUgc3VidHJhY3Rpb24gd29ya2VkIGNvcnJlY3RseQpgYGB7cn0KZGF0MSAlPiUgZmlsdGVyKGhvc3QgPT0gIjE1NiIpICU+JSAKICBwaXZvdF9sb25nZXIoQkwxLkg6blJGUCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBtdXRhdGUocGFyYW1ldGVyID0gb3JkZXJlZChwYXJhbWV0ZXIsIGxldmVscyA9IGMoIkJMMS5IIiwgIllMMi5IIiwgIm5HRlAiLCAiblJGUCIpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBpbnRlbnNpdHkpKSArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gd2VsbCkpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJwb2ludHJhbmdlIiwgY29sb3IgPSAicmVkIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4xKSkgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlciwgc2NhbGUgPSAiZnJlZV95IikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KQoKIyByZW1vdmUgdGhlIHlIMTU2IHNhbXBsZXMKZGF0MSA8LSBkYXQxICU+JSAKICBmaWx0ZXIoaG9zdCAhPSAiMTU2IikgJT4lIAogIG11dGF0ZShob3N0ID0gb3JkZXJlZChob3N0LCBsZXZlbHMgPSBjKCI1NTUiLCAiMzczIiksIGxhYmVscyA9IGMoIlBITzIiLCAicGhvMuKIhiIpKSkKYGBgCgpFeHBvcnQgdGhlIGJhY2tncm91bmQtc3VidHJhY3RlZCB2YWx1ZXMgZm9yIGxhdGVyIHVzZSB3aXRoIHRoZSBzaGlueWFwcApgYGB7cn0Kd3JpdGVfdHN2KGRhdDEsICIuLi9zaGlueWFwcC8yMDIyMDQxMi1iZy1zdWItZGF0YS50c3YiKQpgYGAKCgojIyAzLiBDb25zaXN0ZW5jeSBhY3Jvc3MgcGxhdGVzLgoKQ2hlY2sgdGhlIGJhY2tncm91bmQtc3VidHJhY3RlZCBmbHVvcmVzY2VuY2UgdmFsdWVzIGZvciB0aGUgaG9zdCBzdHJhaW5zICh5SDM3MyBhbmQgeUg1NTUpIGFzIHdlbGwgYXMgdGhlIHBvc2l0aXZlIGNvbnRyb2wgc3RyYWluIChwSDE4OCBpbiB5SDM3MyBvciB5SDU1NSBiYWNrZ3JvdW5kcyksIGJvdGggb2Ygd2hpY2ggYXJlIHByZXNlbnQgb24gZWFjaCBwbGF0ZS4KCmBgYHtyfQpkYXQxICU+JSBmaWx0ZXIocGxhc21pZCAlaW4lIGMoIjE4OCIsICJOQSIpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKEJMMS5IOllMMi5ILCBuYW1lc190byA9ICJwYXJhbWV0ZXIiLCB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikgJT4lIAogICNtdXRhdGUocGFyYW1ldGVyID0gb3JkZXJlZChwYXJhbWV0ZXIsIGxldmVscyA9IGMoIkJMMS5IIiwgIllMMi5IIiwgIm5HRlAiLCAiblJGUCIpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHBsYXNtaWQsIHkgPSBpbnRlbnNpdHksIGNvbG9yID0gZGF0ZSkpICsgCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsgCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAyKSArCiAgZmFjZXRfZ3JpZChwYXJhbWV0ZXJ+aG9zdCwgc2NhbGUgPSAiZnJlZV95IikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9IDMpKQpgYGAKCj4gMS4gVGhlIGhvc3Qgc3RyYWluIGZsdW9yZXNjZW5jZSBsZXZlbHMgYXBwZWFyIHRvIGJlIGNvbnNpc3RlbnQgYWNyb3NzIHRoZSB0aHJlZSBkYXlzLiBBZGRpdGlvbmFsbHksIHRoZWlyIEdGUCBsZXZlbHMgYXJlIHplcm8sIGFzIGV4cGVjdGVkLgo+IDEuIFRoZSBwb3NpdGl2ZSBjb250cm9sIHN0cmFpbiBoYXMgc3Ryb25nIEJMMS5IIGFuZCBjb3JyZXNwb25kaW5nbHkgaGFzIHN0cm9uZyBZTDIuSC4KClRoZSBzaXplIG5vcm1hbGl6ZWQgdmFsdWVzIHNob3cgc2xpZ2h0bHkgbG93ZXIgdmFyaWFuY2U6CmBgYHtyfQpkYXQxICU+JSBmaWx0ZXIocGxhc21pZCAlaW4lIGMoIjE4OCIsICJOQSIpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKG5HRlA6blJGUCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICAjbXV0YXRlKHBhcmFtZXRlciA9IG9yZGVyZWQocGFyYW1ldGVyLCBsZXZlbHMgPSBjKCJCTDEuSCIsICJZTDIuSCIsICJuR0ZQIiwgIm5SRlAiKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwbGFzbWlkLCB5ID0gaW50ZW5zaXR5LCBjb2xvciA9IGRhdGUpKSArIAogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjUpKSArIAogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gMikgKwogIGZhY2V0X2dyaWQocGFyYW1ldGVyfmhvc3QsIHNjYWxlID0gImZyZWVfeSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAzKSkKYGBgCiMjIDQuIFdlbGwgcG9zaXRpb24KCioqX1JhdGlvbmFsZV8qKgoKLSBJbiBteSBvcmlnaW5hbCBwbGF0ZSBkZXNpZ24sIEkgcGxhY2VkIGEgcG9zaXRpdmUgY29udHJvbCBzdHJhaW4gKGVudmlzaW9uZWQgQ2dQaG80LW1OZW9uIGluIF9QSE8yXyBiYWNrZ3JvdW5kIGZvciBleGFtcGxlKSBpbiB0aGUgY29udHJvbCBjb2x1bW5zICgxLCA1LCA5KSBvbiBldmVyeSBvdGhlciByb3cgKEEsIEMsIEUsIEcpLiBUaGUgcmVhc29uIGZvciB0aGlzIGlzIHRvIHVzZSBpdCB0byBpZGVudGlmeSBhbnkgd2VsbCBwb3NpdGlvbiBlZmZlY3Qgb24gdGhlIGZsdW9yZXNjZW5jZSByZWFkaW5ncy4KLSBJbiBFbWlseSdzIGltcGxlbWVudGF0aW9uLCBzaGUgaW5zdGVhZCBwdXQgKiphIHBhaXIqKiBvZiBzdHJhaW5zLCBuYW1lbHkgcEgxODgteUgzMjMgYW5kIHBIMTg4LXlINTU1IGluIHRoZXNlIHdlbGxzLiBUaGlzIG1lYW5zIEkgY2Fubm90IHVzZSB0aGUgcG9zaXRpdmUgY29udHJvbCB3ZWxscyBleGFjdGx5IHRoZSB3YXkgYXMgSSBkZXNpZ25lZCB0aGVtLiBCdXQgSSBjYW4gc3RpbGwgdXNlIHRoZW0gdG8gc3BvdCBhbnkgdHJlbmQuCiAgICAKYGBge3J9CmRhdDEgJT4lIAogIGZpbHRlcihwbGFzbWlkID09ICIxODgiKSAlPiUgCiAgc2VwYXJhdGUod2VsbCwgaW50byA9IGMoInJvdyIsICJjb2wiKSwgc2VwID0gMSkgJT4lIAogIHBpdm90X2xvbmdlcihCTDEuSDpZTDIuSCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSByb3csIHkgPSBpbnRlbnNpdHksIGdyb3VwID0gZGF0ZSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkYXRlKSwgc2l6ZSA9IDEpICsgCiAgI2dlb21fbGluZShhZXMoY29sb3IgPSBkYXRlKSwgc2l6ZSA9IDAuNCkgKwogIHN0YXRfc3VtbWFyeShhZXMoY29sb3IgPSBkYXRlKSwgZ2VvbSA9ICJsaW5lIiwgZnVuID0gbWVhbikgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gMikgKwogIGZhY2V0X2dyaWQocGFyYW1ldGVyfmhvc3QsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTQpCmBgYAoKPiAtIG5vIG9iaXZvdXMgdHJlbmQgYmV0d2VlbiB0aGUgcm93cyAob3IgY29sdW1ucywgbm90IHNob3duKQo+IC0gYSBjbGVhciBjb3JyZWxhdGlvbiBiZXR3ZWVuIFlMMi5IIGFuZCBCTDEuSC4gYXQgbGVhc3QgcGFydCBvZiB0aGlzIGlzIGR1ZSB0byB0aGUgY2VsbCBzaXplIGRpZmZlcmVuY2VzIC0tIHNlZSBzaXplIG5vcm1hbGl6ZWQgZGF0YSBiZWxvdzoKCmBgYHtyfQpkYXQxICU+JSAKICBmaWx0ZXIocGxhc21pZCA9PSAiMTg4IikgJT4lIAogIHNlcGFyYXRlKHdlbGwsIGludG8gPSBjKCJyb3ciLCAiY29sIiksIHNlcCA9IDEpICU+JSAKICBwaXZvdF9sb25nZXIoYyhZTDIuSCwgblJGUCksIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcm93LCB5ID0gaW50ZW5zaXR5LCBncm91cCA9IGRhdGUpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZGF0ZSksIHNpemUgPSAxKSArIAogICNnZW9tX2xpbmUoYWVzKGNvbG9yID0gZGF0ZSksIHNpemUgPSAwLjQpICsKICBzdGF0X3N1bW1hcnkoYWVzKGNvbG9yID0gZGF0ZSksIGdlb20gPSAibGluZSIsIGZ1biA9IG1lYW4pICsKICBzY2FsZV9jb2xvcl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDIpICsKICBmYWNldF9ncmlkKHBhcmFtZXRlcn5ob3N0LCBzY2FsZXMgPSAiZnJlZV95IikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KQpgYGAKCi0gV2hhdCB3ZSBjYXJlIGFib3V0IGlzIHRoZSByYXRpbyBiZXR3ZWVuIFBobzQtbU5lb24gYW5kIF9QSE81cHJfLW1DaGVycnkKCmBgYHtyfQpkYXQxICU+JSAKICBmaWx0ZXIocGxhc21pZCA9PSAiMTg4IikgJT4lIAogIHNlcGFyYXRlKHdlbGwsIGludG8gPSBjKCJyb3ciLCAiY29sIiksIHNlcCA9IDEpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBCTDEuSCwgeSA9IFlMMi5IKSkgKyAKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGRhdGUsIGNvbG9yID0gcm93KSwgc2l6ZSA9IDMpICsgCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiZ3JheTUwIiwgc2l6ZSA9IDAuNSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZCgpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gMTU6MTcpICsKICAjc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpICsKICAjZmFjZXRfZ3JpZChwYXJhbWV0ZXJ+aG9zdCwgc2NhbGVzID0gImZyZWVfeSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkKYGBgCgo+IC0gVGhlcmUgaXMgdmFyaWF0aW9uIGluIEJMMS5IIGFuZCBjb3JyZXNwb25kaW5nbHkgaW4gWUwyLkgsIGJ1dCB0aGUgcmF0aW8gYmV0d2VlbiB0aGUgdHdvIGFyZSB2ZXJ5IGNvbnNpc3RlbnQKCkxldCdzIGNoZWNrIGJvdGggU2NQaG80IChwSDE5NCkgYW5kIENnUGhvNCAocEgxODgpIHRvIHNlZSBpZiB0aGVpciBiZWhhdmlvcnMgYXJlIGNvbnNpc3RlbnQgYWNyb3NzIHRoZSBkYXlzLgpgYGB7cn0KZGF0MSAlPiUgCiAgZmlsdGVyKHBsYXNtaWQgJWluJSBjKCIxODgiLCAiMTk0IikpICU+JSAKICBtdXRhdGUoYFlMMi5IL0JMMS5IYCA9IFlMMi5IL0JMMS5ILAogICNtdXRhdGUoYFlMMi5IL0JMMS5IYCA9IG5SRlAvbkdGUCwKICAgICAgICAgUGhvNCA9IGZhY3RvcihwbGFzbWlkLCBsZXZlbHMgPSBjKCIxOTQiLCAiMTg4IiksIAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlNjUGhvNCIsICJDZ1BobzQiKSkpICU+JSAKICBzZXBhcmF0ZSh3ZWxsLCBpbnRvID0gYygicm93IiwgImNvbCIpLCBzZXAgPSAxKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaG9zdCwgeSA9IGBZTDIuSC9CTDEuSGApKSArIAogIGdlb21fcG9pbnQoY29sb3IgPSBhbHBoYSgiZ3JheTUwIiwgMC45KSwgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIoMC4yKSkgKyAKICBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSAibWVhbl9zZSIsIGdlb20gPSAicG9pbnRyYW5nZSIsIGNvbG9yID0gInJlZCIpICsKICBmYWNldF9ncmlkKFBobzR+ZGF0ZSkgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KQpgYGAKCiMjIDUuIEhpZ2ggdmFyaWFuY2Ugc2FtcGxlcwoKU3VtbWFyaXplIHRoZSBiYWNrZ3JvdW5kIHN1YnRyYWN0ZWQgZGF0YSBieSBjYWxjdWxhdGluZyB0aGUgbWVhbnMgYW5kIGN2IGZvciBlYWNoIHN0cmFpbi4KYGBge3J9CmRhdDIgPC0gZGF0MSAlPiUgCiAgc2VsZWN0KC1uR0ZQLCAtblJGUCkgJT4lIAogIHBpdm90X2xvbmdlcihGU0MuSDpZTDIuSCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkLCBob3N0LCBwYXJhbWV0ZXIpICU+JSAKICBzdW1tYXJpemUoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG51bShtZWFuKGludGVuc2l0eSksIGRpZ2l0cyA9IDApLAogICAgY3YgPSBudW0oc2QoaW50ZW5zaXR5KS9tZWFuKGludGVuc2l0eSksIGRpZ2l0cyA9IDIpCiAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKGN2KSkKYGBgCgpgYGB7cn0KZGF0MiAlPiUgCiAgZmlsdGVyKHBsYXNtaWQgIT0gIk5BIiwgcGFyYW1ldGVyICE9ICJGU0MuSCIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjdikpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLi8yMCkpICsgc3RhdF9lY2RmKCkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjgsIGxpbmV0eXBlID0gMikgKwogIHlsYWIoImN1bXVsYXRpdmUgZGVuc2l0eSIpICsgCiAgZmFjZXRfd3JhcCh+cGFyYW1ldGVyKSArCiAgdGhlbWVfY293cGxvdCgpCmBgYAoKPiBUaGUgaGlzdG9ncmFtJ3MgeS1heGlzIGlzIG5vdCBzaG93bi4gVGhlIGxpbmUgZ3JhcGggcmVwcmVzZW50cyB0aGUgZW1waXJpY2FsIENERiwgYW5kIHRoZSBkb3R0ZWQgaG9yaXpvbnRhbCBsaW5lIGlzIGF0IDgwJS4gR0ZQIGlzIG1vcmUgdmFyaWFibGUgdGhhbiBSRlAsIGxpa2VseSBiZWNhdXNlIHRoZSBhYnNvbHV0ZSB2YWx1ZXMgb2YgdGhlIGZvcm1lciBpcyBsb3dlci4gRm9yIGJvdGgsIH44MCUgb2YgdGhlIHNhbXBsZXMgaGF2ZSBhIENWIDwgMjAlLgoKRG8gdGhlIHNhbWUgZm9yIHRoZSBjZWxsIHNpemUtbm9ybWFsaXplZCB2YWx1ZXMKYGBge3J9Cm5kYXQyIDwtIGRhdDEgJT4lIAogIHNlbGVjdCgtQkwxLkgsIC1ZTDIuSCkgJT4lIAogIHBpdm90X2xvbmdlcihuR0ZQOm5SRlAsIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgcGxhc21pZCwgaG9zdCwgcGFyYW1ldGVyKSAlPiUgCiAgc3VtbWFyaXplKAogICAgbiA9IG4oKSwKICAgIG1lYW4gPSBudW0obWVhbihpbnRlbnNpdHkpLCBkaWdpdHMgPSAwKSwKICAgIGN2ID0gbnVtKHNkKGludGVuc2l0eSkvbWVhbihpbnRlbnNpdHkpLCBkaWdpdHMgPSAyKQogICkgJT4lIAogIGFycmFuZ2UoZGVzYyhjdikpCmBgYAoKYGBge3J9Cm5kYXQyICU+JSAKICBmaWx0ZXIocGxhc21pZCAhPSAiTkEiLCBwYXJhbWV0ZXIgIT0gIkZTQy5IIikgJT4lIAogIGdncGxvdChhZXMoeCA9IGN2KSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IC4uZGVuc2l0eS4uLzIwKSkgKyBzdGF0X2VjZGYoKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuOCwgbGluZXR5cGUgPSAyKSArCiAgeWxhYigiY3VtdWxhdGl2ZSBkZW5zaXR5IikgKyAKICBmYWNldF93cmFwKH5wYXJhbWV0ZXIpICsKICB0aGVtZV9jb3dwbG90KCkKYGBgCgo+IENWIGlzIHNtYWxsZXIgb24gdGhlIGNlbGwgc2l6ZSBub3JtYWxpemVkIGZsdW9yZXNjZW5jZSB2YWx1ZXMuCgojIE1haW4KIyMgUGxvdHRpbmcgZnVuY3Rpb25zCkhlcmUgSSdkIGxpa2UgdG8gZGV2ZWxvcCBhIHNlcmllcyBvZiBwbG90dGluZyBmdW5jdGlvbnMgdGhhdCB0YWtlIHRoZSBuYW1lcyBvciBhbnkgcGFydCBvZiB0aGUgUGhvNCBjaGltZXJhIGFubm90YXRpb24gYXMgaW5wdXQgYW5kIHBsb3QgdGhlaXIgcmVzdWx0cyBpbiBhIHZhcmlldHkgb2Ygd2F5cwpMb2FkIHRoZSBQaG80IHBsYXNtaWQgaW5mb3JtYXRpb24gYW5kIG1lcmdlIHdpdGggdGhlIHJlc2hhcGVkIGRhdGEKYGBge3J9Cm1ldGEgPC0gcmVhZF90c3YoIi4uL2RhdGEvMjAyMjAzMzAtY2hpbWVyYS1QaG80LW1ha2V1cC50c3YiLCBjb2xfdHlwZXMgPSAiY2NjYyIpCmBgYAoKUGxvdHRpbmcgZnVuY3Rpb24KYGBge3J9CiMgZXh0cmFjdCB4aW1lcmEgbmFtZXMKcmVmcyA8LSBjKCJDQ0NDQyIsIlNTU1NTIikKeGltZXJhcyA8LSBzZXRkaWZmKG1ldGEkc3ltYm9sLCByZWZzKQojIG1ha2UgYSB0ZXN0IHNldAp0ZXN0IDwtIHNhbXBsZSh4aW1lcmFzLCA2KQojIHN1YnNldCBkYXRhCnRtcCA8LSBtZXRhICU+JSAKICBmaWx0ZXIoc3ltYm9sICVpbiUgYyhyZWZzLHRlc3QpKSAlPiUgCiAgaW5uZXJfam9pbihkYXQxLCBieSA9ICJwbGFzbWlkIikgJT4lIAogIG11dGF0ZShzeW1ib2wgPSBmYWN0b3Ioc3ltYm9sLCBsZXZlbHMgPSBjKHJlZnMsIHRlc3QpKSkKYGBgCgpQbG90IGluZGl2aWR1YWwgY29tcG9uZW50cwpgYGB7cn0KdG1wICU+JSAKICBwaXZvdF9sb25nZXIobkdGUDpuUkZQLCBuYW1lc190byA9ICJwYXJhbWV0ZXIiLCB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikgJT4lIAogIG11dGF0ZShwYXJhbWV0ZXIgPSBvcmRlcmVkKHBhcmFtZXRlciwgbGV2ZWxzID0gYygiblJGUCIsICJuR0ZQIikpKSAlPiUgCiAgI3Bpdm90X2xvbmdlcihCTDEuSDpZTDIuSCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICAjbXV0YXRlKHBhcmFtZXRlciA9IG9yZGVyZWQocGFyYW1ldGVyLCBsZXZlbHMgPSBjKCJZTDIuSCIsICJCTDEuSCIpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHN5bWJvbCwgeSA9IGludGVuc2l0eSwgZ3JvdXAgPSBob3N0KSkgKyAKICBnZW9tX2JhcihhZXMoZmlsbCA9IGhvc3QpLCB3aWR0aCA9IDAuNSwgYWxwaGEgPSAwLjgsCiAgICAgICAgICAgc3RhdCA9ICJzdW1tYXJ5IiwgZnVuID0gIm1lYW4iLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGhvc3QpLCBzaXplID0gMSwKICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGggPSAwLjUpKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJQSE8yIiA9ICJncmF5MjAiLCAicGhvMuKIhiIgPSAiZ3JheTQwIikpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChiZWdpbiA9IDAuMiwgZW5kID0gMC42KSArCiAgI3N0YXRfc3VtbWFyeShmdW4gPSAibWVhbiIsIGdlb20gPSAiY3Jvc3NiYXIiLCBjb2xvciA9ICJyZWQiLCB3aWR0aCA9IDAuMjUsCiAgIyAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNzUpLCApICsKICBmYWNldF93cmFwKH5wYXJhbWV0ZXIsIHNjYWxlID0gImZyZWVfeSIsIG5jb2wgPSAxKSArCiAgeGxhYigiUGhvNCBjaGltZXJhIikgKyBleHBhbmRfbGltaXRzKHkgPSAwKSArCiAgdGhlbWVfZ3JheShiYXNlX3NpemUgPSAxNCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSwgZmFtaWx5ID0gIm1vbm8iKSkKYGBgCmBgYHtyfQp0bXAgJT4lIAogICNtdXRhdGUoYFlMMi5IL0JMMS5IYCA9IFlMMi5IL0JMMS5ILAogIG11dGF0ZShgUi9HYCA9IG5SRlAvbkdGUCkgJT4lIAogIGdncGxvdChhZXMoeCA9IHN5bWJvbCwgeSA9IGBSL0dgLCBncm91cCA9IGhvc3QpKSArIAogIGdlb21fYmFyKGFlcyhmaWxsID0gaG9zdCksIHdpZHRoID0gMC41LCBhbHBoYSA9IDAuOCwKICAgICAgICAgICBzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gaG9zdCksIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGggPSAwLjUpKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJQSE8yIiA9ICJncmF5MjAiLCAicGhvMuKIhiIgPSAiZ3JheTQwIikpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChiZWdpbiA9IDAuMiwgZW5kID0gMC42KSArCiAgI3N0YXRfc3VtbWFyeShmdW4gPSAibWVhbiIsIGNvbG9yID0gInJlZCIsIGdlb20gPSAiY3Jvc3NiYXIiLCB3aWR0aCA9IDAuMiwKICAjICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC43NSksICkgKwogIHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMTQpICsgeGxhYigiUGhvNCBjaGltZXJhIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSwgZmFtaWx5ID0gIm1vbm8iKSkKYGBgCgpOZXh0IGlkZWEgaXMgdG8gcGxvdCB0aGUgcmF0aW8gYmV0d2VlbiB0aGUgbWVhbiBSL0cgZm9yIF9QSE8yXyBhbmQgbWVhbiBSL0cgZm9yIF9waG8y4oiGXywgb3ZlciB0aGUgbWVhbiBSL0cgZm9yIF9QSE8yXwoKRmlyc3Qgc3VtbWFyaXplIHRoZSBkYXRhLgpgYGB7cn0KZGF0c3VtIDwtIGRhdDEgJT4lCiAgZmlsdGVyKHBsYXNtaWQgIT0gIk5BIikgJT4lIAogIG11dGF0ZShgUi9HYCA9IFlMMi5IL0JMMS5ILCBgblIvR2AgPSBuUkZQL25HRlApICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkLCBob3N0KSAlPiUgCiAgc3VtbWFyaXplKGFjcm9zcyhjKEJMMS5ILCBuR0ZQLCBZTDIuSCwgblJGUCwgYFIvR2AsIGBuUi9HYCksIG1lYW4pLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGhvc3QsIHZhbHVlc19mcm9tID0gQkwxLkg6YG5SL0dgKSAlPiUgCiAgbXV0YXRlKGBwaG8y4oiGL1BITzJgID0gYFIvR19waG8y4oiGYC9gUi9HX1BITzJgLAogICAgICAgICBgbi5waG8y4oiGL1BITzJgID0gYG5SL0dfcGhvMuKIhmAvYG5SL0dfUEhPMmApCgojIHVzZWZ1bCB0byBzZXQgYSBmbGFnIGZvciBsb3cgYWN0aXZpdHkgbXV0YW50cwpsb3cuYWN0LnRoIDwtIGRhdHN1bSAlPiUgZmlsdGVyKHBsYXNtaWQgPT0gIjE5NCIpICU+JSBzdW1tYXJpemUoYWNyb3NzKC5jb2xzID0gd2hlcmUoaXMubnVtZXJpYyksIC5mbnMgPSBtZWFuKSkgJT4lIHVubGlzdCgpCmBgYAoKVGhlbiBleHRyYWN0IHRoZSBzdWJzZXQgZm9yIHBsb3R0aW5nCmBgYHtyfQp0bXBzdW0gPC0gbWV0YSAlPiUgCiAgZmlsdGVyKHN5bWJvbCAlaW4lIGMocmVmcyx0ZXN0KSkgJT4lIAogIGlubmVyX2pvaW4oZGF0c3VtLCBieSA9ICJwbGFzbWlkIikgJT4lIAogIG11dGF0ZShzeW1ib2wgPSBmYWN0b3Ioc3ltYm9sLCBsZXZlbHMgPSBjKHJlZnMsIHRlc3QpKSkKCmBgYAoKRGVzaWduIHRoZSBwbG90CmBgYHtyfQp0bXBzdW0gJT4lIAogIG11dGF0ZShBY3Rpdml0eSA9IGlmZWxzZShgUi9HX1BITzJgIDwgMipsb3cuYWN0LnRoWyJSL0dfcGhvMuKIhiJdLCAibG93IiwgInBhc3MiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHN5bWJvbCwgeSA9IGBwaG8y4oiGL1BITzJgKSkgKyAKICBnZW9tX2NvbChhZXMoZ3JvdXAgPSBkYXRlLCBmaWxsID0gYFIvR19QSE8yYCksIHdpZHRoID0gMC43NSwgY29sb3IgPSAiZ3JheTUwIiwKICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICAjZ2VvbV9wb2ludChhZXMoY29sb3IgPSBob3N0KSwgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXJkb2RnZShkb2RnZS53aWR0aCA9IDAuNSkpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoIkFjdGl2aXR5IikgKwogICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhhbHBoYSgiYmxhY2siLDApLCAicmVkMyIpKSArCiAgI3N0YXRfc3VtbWFyeShmdW4gPSAibWVhbiIsIGNvbG9yID0gInJlZCIsIGdlb20gPSAiY3Jvc3NiYXIiLCB3aWR0aCA9IDAuMiwKICAjICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC43NSksICkgKwogIGZhY2V0X2dyaWQoLn5BY3Rpdml0eSwgc2NhbGVzID0gImZyZWVfeCIsIHNwYWNlID0gImZyZWVfeCIsIGxhYmVsbGVyID0gImxhYmVsX2JvdGgiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTQpICsgeGxhYigiUGhvNCBjaGltZXJhIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSwgZmFtaWx5ID0gIm1vbm8iKSkKYGBgCgpYLVkgcGxvdApgYGB7cn0KcDMgPC0gdG1wc3VtICU+JSAKICBtdXRhdGUoYG5SL0dfUEhPMmAgPSBzaWduaWYoYG5SL0dfUEhPMmAsIGRpZ2l0cyA9IDIpLAogICAgICAgICBgblIvR19waG8y4oiGYCA9IHNpZ25pZihgblIvR19waG8y4oiGYCwgZGlnaXRzID0gMikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBgblIvR19QSE8yYCwgeSA9IGBuUi9HX3BobzLiiIZgLCBsYWJlbCA9IHN5bWJvbCkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMi41KSArIGdlb21fYWJsaW5lKHNsb3BlID0gMSkgKwogIHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMTQpCmdncGxvdGx5KHAzLCB0b29sdGlwID0gYygibGFiZWwiLCAieCIsICJ5IikpCmBgYAoK